package util;

import com.intellij.database.dialects.mysqlbase.model.MysqlBaseTableColumn;
import com.intellij.database.model.DasObject;
import com.intellij.database.model.ObjectKind;
import com.intellij.database.model.basic.BasicKey;
import com.intellij.database.model.basic.BasicMixinTableOrView;
import com.intellij.database.model.basic.BasicTableColumn;
import com.intellij.database.psi.DbElement;
import com.intellij.database.psi.DbNamespaceImpl;
import com.intellij.database.util.DatabaseDefinitionHelper;
import com.intellij.psi.PsiElement;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.JBIterable;
import enums.ColumnType;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * @Created by haoqi
 */
public class SqlUtil {
    public static final String SOURCE = "source";
    public static final String TARGET = "targets";


    public static StringJoiner getDiffTables(PsiElement source, Map<String, Object> map) {
        StringJoiner joiner = new StringJoiner(";\n");

        joiner.add("# delete Fields *");
        Map deleteFields = (HashMap) map.get("deleteFields");
        deleteFields.forEach((tableName, diffFields) -> {
            //fieldsKey 字段名称
            //fieldsValue 属性/值集合
            ((HashMap) diffFields).forEach((fieldsKey, fieldsValue) -> {
                joiner.add(dropColumns(fieldsValue));
            });
        });

        joiner.add("# drop table *");
        List<String> dropTables = (ArrayList<String>) map.get("dropTables");
        dropTables.forEach(tableName -> {
            joiner.add("drop table " + tableName + ";");
        });

        joiner.add("# diff Fields *");
        Map diffTables = (HashMap) map.get("diffFields");
        diffTables.forEach((tableName, diffFields) -> {
            //fieldsKey 字段名称
            //fieldsValue 属性/值集合
            ((HashMap) diffFields).forEach((fieldsKey, fieldsValue) -> {
                joiner.add(modifyColumns(fieldsValue));
            });
        });

        joiner.add("# new Tables *");
        List<String> newTables = (ArrayList<String>) map.get("newTables");
        joiner.add(getNewTables(source, newTables));
        return joiner;
    }


    static String getNewTables(PsiElement psiElement, List<String> newTables) {
        StringJoiner stringJoiner = new StringJoiner("\n");
        DbElement dbElement = ObjectUtils.tryCast(psiElement, DbElement.class);
        JBIterable<? extends DbElement> dasChildren = dbElement.getDasChildren(ObjectKind.TABLE);
        for (DbElement dasChild : dasChildren) {
            if (newTables.contains(dasChild.getName())) {
                stringJoiner.add(DatabaseDefinitionHelper.generateDefinition(dasChild, new StringBuilder()));
            }
        }
        return stringJoiner.toString();
    }


    private static String modifyColumns(Object fieldsValue) {
        StringBuilder builder = new StringBuilder();
        HashMap field = (HashMap) fieldsValue;
        HashMap<ColumnType, String> fieldVal;
        if (field.keySet().size() == 2) {
            //存在字段差异
            fieldVal = (HashMap<ColumnType, String>) field.get(SqlUtil.SOURCE);
            builder.append("alter table ")
                    .append(fieldVal.get(ColumnType.TableName))
                    .append(" modify ");

        } else {
            fieldVal = (HashMap<ColumnType, String>) field;
            builder.append("alter table ")
                    .append(fieldVal.get(ColumnType.TableName))
                    .append(" add ");
        }

        builder.append(fieldVal.get(ColumnType.Field))
                .append(" ")
                .append(fieldVal.get(ColumnType.Type));
        if (StringUtils.equals(fieldVal.get(ColumnType.Default), null)
                && !StringUtils.equals(fieldVal.get(ColumnType.Null), "true")) {
        } else {
            if (StringUtils.contains(fieldVal.get(ColumnType.Default), "'")
                    || StringUtils.equals(fieldVal.get(ColumnType.Type), "datetime")
                    || StringUtils.equals(fieldVal.get(ColumnType.Type), "timestamp")
                    || StringUtils.equals(fieldVal.get(ColumnType.Default), null)) {
                builder.append(" default ");
                builder.append(fieldVal.get(ColumnType.Default));
            } else {
                builder.append(" default ");
                builder.append("'").append(fieldVal.get(ColumnType.Default)).append("'");
            }
        }


        if (StringUtils.isNotEmpty(fieldVal.get(ColumnType.Extra))) {
            builder.append(" ").append(fieldVal.get(ColumnType.Extra));
        }

        if (StringUtils.equals(fieldVal.get(ColumnType.Null), "true")) {
            builder.append(" NULL ");
        } else {
            builder.append(" NOT NULL ");
        }

        if (StringUtils.isNotEmpty(fieldVal.get(ColumnType.Comment))) {
            builder.append("comment '").append(fieldVal.get(ColumnType.Comment)).append("'");
        }
        return builder.toString();
    }

    //alter table gc_levy_tax_detail_excel_template drop column file_path;
    private static String dropColumns(Object fieldsValue) {
        StringBuilder builder = new StringBuilder();
        HashMap field = (HashMap) fieldsValue;
        HashMap<ColumnType, String> fieldVal;

        builder.append("alter table ")
                .append(field.get(ColumnType.TableName))
                .append(" drop column ");

        builder.append(field.get(ColumnType.Field))
                .append(";");
        return builder.toString();
    }

    public static Map<String, Map<String, Map<ColumnType, Object>>> getData(DbNamespaceImpl source) {
        Map<String, Map<String, Map<ColumnType, Object>>> tables = new HashMap<>();

        DasObject delegate = source.getDelegate();
        JBIterable<? extends DasObject> dasChildren = delegate.getDasChildren(ObjectKind.TABLE);
        for (DasObject dasChild : dasChildren) {
            JBIterable<? extends DasObject> dasChildren1 = dasChild.getDasChildren(ObjectKind.COLUMN);
            List<String> colNames = new ArrayList<>();

            BasicKey primaryKey = ((BasicMixinTableOrView) dasChild).getPrimaryKey();
            if (primaryKey != null) {
                colNames.addAll(primaryKey.getColNames());
            }
            Map<String, Map<ColumnType, Object>> table = new HashMap<>();

            for (DasObject dasObject : dasChildren1) {
                BasicTableColumn basicTableColumn = (BasicTableColumn) dasObject;

                Map<ColumnType, Object> map = new HashMap<>();
                map.put(ColumnType.TableName, dasChild.getName());
                map.put(ColumnType.Field, dasObject.getName());
                map.put(ColumnType.Type, basicTableColumn.getDataType().getSpecification());
//                map.put(ColumnType.Type, basicTableColumn.getDataType().getSpecification() + "(" + basicTableColumn.getPosition() + ")");
                map.put(ColumnType.Null, Boolean.toString(!basicTableColumn.isNotNull()));
                if (colNames.contains(dasObject.getName())) {
                    map.put(ColumnType.Key, "PRI");
                }
                map.put(ColumnType.Default, basicTableColumn.getDefaultExpression());

                if (basicTableColumn instanceof MysqlBaseTableColumn) {
                    MysqlBaseTableColumn mysqlBaseTableColumn = (MysqlBaseTableColumn) basicTableColumn;
                    if (StringUtils.isNotBlank(mysqlBaseTableColumn.getOnUpdate())) {
                        map.put(ColumnType.Extra, mysqlBaseTableColumn.getOnUpdate());
                    }
                }

                map.put(ColumnType.Comment, basicTableColumn.getComment());
                table.put(dasObject.getName(), map);
            }
            tables.put(dasChild.getName(), table);
        }


        return tables;
    }
}
